﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using Expect.VisualStudio.Extensions;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Classification;
using Microsoft.VisualStudio.Utilities;

namespace Expect.Vs2010.Extensions
{    
    #region Provider definition
    /// <summary>
    /// This class causes a classifier to be added to the set of classifiers. Since 
    /// the content type is set to "text", this classifier applies to all text files
    /// </summary>
    [Export(typeof(IClassifierProvider))]
    [ContentType(Constants.ExpectContentType)]
    internal class ExpectClassifierProvider : IClassifierProvider
    {
        /// <summary>
        /// Import the classification registry to be used for getting a reference
        /// to the custom classification type later.
        /// </summary>
        [Import]
        internal IClassificationTypeRegistryService ClassificationRegistry = null; // Set via MEF

        public IClassifier GetClassifier(ITextBuffer buffer)
        {
            return buffer.Properties.GetOrCreateSingletonProperty<ExpectEditorClassifier>(delegate { return new ExpectEditorClassifier(ClassificationRegistry); });
        }
    }
    #endregion //provider def

    #region Classifier
    /// <summary>
    /// Classifier that classifies all text as an instance of the OrinaryClassifierType
    /// </summary>
    class ExpectEditorClassifier : IClassifier
    {
        IClassificationType _classificationType;
        IClassificationType _keywordsClassificationType;
        IClassificationType _keywordsParamClassificationType;

        [Import]
        internal IServiceProvider ServiceProvider; 


        internal ExpectEditorClassifier(IClassificationTypeRegistryService registry)
        {
            _classificationType = registry.GetClassificationType("ExpectEditorClassification");
            _keywordsClassificationType = registry.GetClassificationType("ExpectEditorKeywordClassification");
            _keywordsParamClassificationType = registry.GetClassificationType("ExpectEditorKeywordParamClassification");
        }

        /// <summary>
        /// This method scans the given SnapshotSpan for potential matches for this classification.
        /// In this instance, it classifies everything and returns each span as a new ClassificationSpan.
        /// </summary>
        /// <param name="trackingSpan">The span currently being classified</param>
        /// <returns>A list of ClassificationSpans that represent spans identified to be of this classification</returns>
        public IList<ClassificationSpan> GetClassificationSpans(SnapshotSpan span)
        {
            //create a list to hold the results
            List<ClassificationSpan> classifications = new List<ClassificationSpan>();
            string txt = span.GetText();
            string txtTrimmed = txt.Trim();

            if (txtTrimmed.StartsWith("#"))
            {
                classifications.Add(
                       new ClassificationSpan(
                           new SnapshotSpan(span.Snapshot, new Span(span.Start, span.Length)), _classificationType)
                           );                
            }
            else if (txtTrimmed.StartsWith("@"))
            {
                classifications.Add(
                       new ClassificationSpan(
                           new SnapshotSpan(span.Snapshot, new Span(span.Start, span.Length)), _keywordsClassificationType)
                           );
            }
            else
            {
                string token = txt.TrimEnd(' ', '\r', '\n');
                int curpos = span.Start;
                int idx = txt.IndexOf("|");
                
                if (idx < 0)
                {
                    return classifications;
                }

                while (token.Length > 0 && idx >= 0)
                {
                    if (idx == 0)
                    {
                        int nextidx = token.IndexOf("|", idx + 1);

                        if (nextidx > 0 && token[idx + 1] != ' ' && token[nextidx - 1] != ' ')
                        {
                            classifications.Add(
                            new ClassificationSpan(
                                new SnapshotSpan(span.Snapshot, new Span(curpos, nextidx+1)), _keywordsParamClassificationType)
                            );
                            curpos += nextidx+1;
                            token = token.Substring(nextidx + 1);
                            idx = token.IndexOf("|");
                            continue;
                        }
                        
                        classifications.Add(
                            new ClassificationSpan(
                                new SnapshotSpan(span.Snapshot, new Span(curpos, 1)), _keywordsParamClassificationType)
                            );
                        curpos += 1;
                        token = token.Substring(1);
                    }
                    else
                    {
                        token = token.Substring(idx);
                        curpos += idx;
                    }


                    idx = token.IndexOf("|");                    
                }
            }

            /*else
            {
                
                var keyword = this.MatchingKeyword(txt, txtTrimmed);
                if (!string.IsNullOrEmpty(keyword))
                {
                    var line = span.Snapshot.GetLineFromPosition(span.Start.Position).Extent;
                    classifications.Add(
                       new ClassificationSpan(
                           line, //new SnapshotSpan(span.Snapshot, new Span(span.Start, txt.Length)),
                            _keywordsClassificationType)
                           );
                }
            }*/
            
            return classifications;
        }

        /*
        private string MatchingKeyword(string txt, string trimmed)
        {
            foreach (var item in Constants.KeyWords)
            {
                if (trimmed.StartsWith(item.TagName))
                {
                    return item.TagName;
                }
            }

            return null;
        }*/

#pragma warning disable 67
        // This event gets raised if a non-text change would affect the classification in some way,
        // for example typing /* would cause the classification to change in C# without directly
        // affecting the span.
        public event EventHandler<ClassificationChangedEventArgs> ClassificationChanged;
#pragma warning restore 67
    }
    #endregion //Classifier
}
